package udpInvoker.util;

import udpInvoker.main.InvocationMessage;
import udpInvoker.main.Base64;

import java.net.*;
import java.io.IOException;
import java.util.Date;

import org.jasypt.util.text.BasicTextEncryptor;

/**
 * Created by IntelliJ IDEA.
 * User: esr
 * Date: 09.okt.2008
 * Time: 15:49:25
 * Copyright 2008  Espen Skjervold, FFI
 */
public class UdpUtils {

    private static MulticastSocket cachedMulticastSocket;
    private static DatagramSocket cachedUnicastSocket;


    public static InvocationMessage receiveMessage(DatagramSocket datagramSocket, String password) throws IOException {
        byte[] buffer=new byte[1000000];
        DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);

        //System.out.println("Waiting for response message...");
        datagramSocket.receive(datagramPacket);


        buffer =  datagramPacket.getData();
        buffer = new String(buffer).trim().getBytes();


        InvocationMessage invocationMessage = deSerializeInvocationMessage(buffer, password);

        return invocationMessage;
    }


    public static byte[] receiveMessageBuffer(int transportType, String address, int port, boolean listen) {

        byte[] buffer = new byte[1000000];




        try {

            DatagramSocket datagramSocket = createDatagramSocket(transportType, address, port, listen);



            DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length);


            datagramSocket.receive(datagramPacket);


            buffer =  datagramPacket.getData();
            buffer = new String(buffer).trim().getBytes();

        }
        catch (Exception e) {
            e.printStackTrace();
        }


        return buffer;
       }

    public static void sendBuffer(byte[] buffer, int transportType, String address, int port) throws Exception{


            DatagramSocket datagramSocket = createDatagramSocket(transportType, address, port, false);


            if (address==null) {  //multicast
                address="224.224.224.223";
                port = 7168;
            }

            DatagramPacket datagramPacket = new DatagramPacket(buffer, buffer.length, new InetSocketAddress(address, port));


            datagramSocket.send(datagramPacket);
            System.out.println("Sent datagram to " + address);

    }

     public static void sendMessage(InvocationMessage responseMessage, boolean useEncryption, String password, int transportType, String address, int port) throws IOException {

            DatagramSocket datagramSocket = createDatagramSocket(transportType, address, port, false);


            if (address==null) {  //multicast
                address="224.224.224.223";
                port = 7168;
            }

            byte[] buffer;
            buffer = Base64.encodeObject(responseMessage, Base64.GZIP).getBytes();

            if (useEncryption)
                buffer = UdpUtils.encryptBuffer(buffer, password);

            DatagramPacket responseDatagram =new DatagramPacket(buffer, buffer.length, new InetSocketAddress(address, port));

            datagramSocket.send(responseDatagram);
        }


    public static InvocationMessage deSerializeInvocationMessage(byte[] buffer, String password) {

                  InvocationMessage invocationMessage = null;



                  if (isEncrypted(buffer) && password!=null) {

                      try {
                          buffer = decryptBuffer(buffer, password);
                          invocationMessage = (InvocationMessage) Base64.decodeToObject(new String(buffer));
                      } catch (Exception e) {
                          e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                      }


                  }
                  else if (!isEncrypted(buffer)){
                      invocationMessage = (InvocationMessage) Base64.decodeToObject(new String(buffer));
                  }




                  return invocationMessage;
              }



    private static boolean isEncrypted(byte[] buffer) {
        String string = new String(buffer);
        if (string.startsWith("CRYPTO"))
            return true;
        else
            return false;
    }



    public static DatagramSocket createDatagramSocket(int transportType, String address, int port, boolean listen) {

        if (transportType==1)
            return createMulticastSocket();
        else
            return createUnicastSocket(address, port, listen);

    }

    public static  DatagramSocket createMulticastSocket()  {

        if (cachedMulticastSocket!=null)
            return cachedMulticastSocket;

        MulticastSocket multicastSocket = null;
        try {
            multicastSocket = new MulticastSocket(7168);
            multicastSocket.setTimeToLive(6);

            InetAddress multicastAddress = InetAddress.getByName("224.224.224.223");

            multicastSocket.joinGroup(multicastAddress);
            cachedMulticastSocket=multicastSocket;

            System.out.println("multicast-socket created");

        } catch (IOException e) {
            e.printStackTrace();
            multicastSocket = cachedMulticastSocket;
        }



        return multicastSocket;
    }

    public static  DatagramSocket createUnicastSocket(String address, int port, boolean listen)  {

        if (cachedUnicastSocket!=null)
            return cachedUnicastSocket;

        DatagramSocket unicastSocket = null;



        try {



            unicastSocket= new DatagramSocket(port);

            cachedUnicastSocket= unicastSocket;





        } catch (Exception e) {
            System.out.println("error thread " + Thread.currentThread().getName() );

            e.printStackTrace();

        }





        return unicastSocket;
    }

    public static InvocationMessage createPingMessage(String methodName, Object... parameters) {
        InvocationMessage pingmMessage = createInvocationMessage(methodName, parameters);
        pingmMessage.setType(InvocationMessage.PING);

        return pingmMessage;
    }

     public static InvocationMessage createInvocationMessage(String methodName, Object... parameters) {
        InvocationMessage invocationMessage =new InvocationMessage();
        invocationMessage.setSerialNumber(new Date().getTime());
        invocationMessage.setType(InvocationMessage.INVOCATION);
        invocationMessage.setMethodName(methodName);

        for (int i=0;i<parameters.length;i++) {
            invocationMessage.addParameter(parameters[i]);
        }
        return invocationMessage;
    }

    public static byte[] encryptBuffer(byte[] buffer, String password) {

           BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();
           basicTextEncryptor.setPassword(password);
           String encryptedString =  basicTextEncryptor.encrypt(new String(buffer));
           encryptedString = "CRYPTO" + encryptedString;

           return encryptedString.getBytes();

       }

     public static byte[] decryptBuffer(byte[] buffer, String password) {
        BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();
        basicTextEncryptor.setPassword(password);
        String encryptedString = new String(buffer).replace("CRYPTO", "");
        buffer = (basicTextEncryptor.decrypt(encryptedString).getBytes());
        return buffer;
    }





}
